package com.jp.knowledge.view;

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.jp.knowledge.R;
import com.jp.knowledge.util.L;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Created by 528787067@qq.com on 2017/7/22.
 */

public class TabView extends HorizontalScrollView implements ViewPager.OnPageChangeListener, View.OnClickListener, ValueAnimator.AnimatorUpdateListener {

	private static final int DEFAULT_INDICATOR_ANIMATOR_DURATION = 200;

	public static final int INDICATOR_FOLLOW = 0;
	public static final int INDICATOR_SELECTED = 1;
	@IntDef({INDICATOR_FOLLOW, INDICATOR_SELECTED})
	@Retention(RetentionPolicy.SOURCE)
	public @interface IndicatorType {}

	public static final int INDICATOR_BOTTOM = 0;
	public static final int INDICATOR_TOP = 1;
	@IntDef({INDICATOR_BOTTOM, INDICATOR_TOP})
	@Retention(RetentionPolicy.SOURCE)
	public @interface IndicatorGravity {}

	public static final int INDICATOR_LINE_TRANSLATE = 0;
	public static final int INDICATOR_LINE_SCALING = 1;
	public static final int INDICATOR_LINE_TRANSLATE_FILL = 2;
	public static final int INDICATOR_LINE_SCALING_FILL = 3;
	public static final int INDICATOR_CIRCLE = 4;
	public static final int INDICATOR_TRIANGLE = 5;
	@IntDef({INDICATOR_LINE_TRANSLATE, INDICATOR_LINE_SCALING, INDICATOR_LINE_TRANSLATE_FILL,
			INDICATOR_LINE_SCALING_FILL, INDICATOR_CIRCLE, INDICATOR_TRIANGLE})
	@Retention(RetentionPolicy.SOURCE)
	public @interface IndicatorAnimator {}

	private List datas;
	private Paint paint;
	private TabItemCreator tabItemCreator;
	private LinearLayout contentLayout;
	private ViewPager viewPager;
	private TabItemClickListener tabItemClickListener;
	private DrawCallback drawCallback;
	private ValueAnimator valueAnimator;
	private volatile float positionOffset;

	private boolean tabAlwaysCenter;
	private float tabLeftMargin;
	private float tabRightMargin;

	private float tabIndicatorHeight;
	private float tabIndicatorTopMargin;
	private float tabIndicatorBottomMargin;
	private int tabIndicatorColor;
	private int tabIndicatorAnimatorDuration;
	private int tabIndicatorType;
	private int tabIndicatorGravity;
	private int tabIndicatorAnimator;

	private float tabTopLineHeight;
	private int tabTopLineColor;
	private float tabTopLineLeftMargin;
	private float tabTopLineRightMargin;
	private float tabTopLineTopMargin;
	private float tabTopLineBottomMargin;

	private float tabBottomLineHeight;
	private int tabBottomLineColor;
	private float tabBottomLineLeftMargin;
	private float tabBottomLineRightMargin;
	private float tabBottomLineTopMargin;
	private float tabBottomLineBottomMargin;

	private float tabSplitLineWidth;
	private int tabSplitLineColor;
	private float tabSplitLineLeftMargin;
	private float tabSplitLineRigahtMargin;
	private float tabSplitLineTopMargin;
	private float tabSplitLineBottomMargin;
	private boolean splitLineSpaceStartEnd;
	private boolean splitLineDrawStartEnd;

	private float tabLeftPadding;
	private float tabRightPadding;
	private int tabSelectTextColor;
	private int tabUnselectTextColor;
	private float tabTextSize;

	public TabView(Context context) {
		super(context);
		init(context, null);
	}

	public TabView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context, attrs);
	}

	public TabView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		init(context, attrs);
	}

	private void init(Context context, AttributeSet attrs) {
		initParam(context, attrs);
		setWillNotDraw(false);
		setHorizontalScrollBarEnabled(false);
		setOverScrollMode(OVER_SCROLL_NEVER);
//		setTabViewSpace();
		paint = new Paint();
		datas = new ArrayList();
		paint.setAntiAlias(true);
		contentLayout = new LinearLayout(context);
		contentLayout.setTag(-1);
		contentLayout.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
		contentLayout.setOrientation(LinearLayout.HORIZONTAL);
		contentLayout.setGravity(Gravity.CENTER_VERTICAL);
		positionOffset = (int) contentLayout.getTag();
		addView(contentLayout);
		initDefaultTabAdapter();
	}

	private void initParam(Context context, AttributeSet attrs){
		TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TabView);
		tabAlwaysCenter = typedArray.getBoolean(R.styleable.TabView_tab_always_center, true);
		tabLeftMargin = typedArray.getDimension(R.styleable.TabView_tab_left_margin, getResources().getDimension(R.dimen.tab_view_left_margin));
		tabRightMargin = typedArray.getDimension(R.styleable.TabView_tab_right_margin, getResources().getDimension(R.dimen.tab_view_right_margin));
		tabIndicatorHeight = typedArray.getDimension(R.styleable.TabView_indicator_height, getResources().getDimension(R.dimen.tab_view_indicator_height));
		tabIndicatorTopMargin = typedArray.getDimension(R.styleable.TabView_indicator_top_margin, getResources().getDimension(R.dimen.tab_view_indicator_top_margin));
		tabIndicatorBottomMargin = typedArray.getDimension(R.styleable.TabView_indicator_bottom_margin, getResources().getDimension(R.dimen.tab_view_indicator_bottom_margin));
		tabIndicatorColor = typedArray.getColor(R.styleable.TabView_indicator_color, ContextCompat.getColor(context, R.color.tab_view_indicator_color));
		tabIndicatorGravity = typedArray.getInt(R.styleable.TabView_indicator_gravity, INDICATOR_BOTTOM);
		tabIndicatorType = typedArray.getInt(R.styleable.TabView_indicator_type, INDICATOR_FOLLOW);
		tabIndicatorAnimator = typedArray.getInt(R.styleable.TabView_indicator_animator, INDICATOR_LINE_TRANSLATE);
		tabIndicatorAnimatorDuration = typedArray.getInteger(R.styleable.TabView_indicator_animator_duration, DEFAULT_INDICATOR_ANIMATOR_DURATION);
		tabTopLineHeight = typedArray.getDimension(R.styleable.TabView_top_line_height, getResources().getDimension(R.dimen.tab_view_top_line_height));
		tabTopLineColor = typedArray.getColor(R.styleable.TabView_top_line_color, ContextCompat.getColor(context, R.color.tab_view_top_line_color));
		tabTopLineLeftMargin = typedArray.getDimension(R.styleable.TabView_top_line_left_margin, getResources().getDimension(R.dimen.tab_view_top_line_left_margin));
		tabTopLineRightMargin = typedArray.getDimension(R.styleable.TabView_top_line_right_margin, getResources().getDimension(R.dimen.tab_view_top_line_right_margin));
		tabTopLineTopMargin = typedArray.getDimension(R.styleable.TabView_top_line_top_margin, getResources().getDimension(R.dimen.tab_view_top_line_top_margin));
		tabTopLineBottomMargin = typedArray.getDimension(R.styleable.TabView_top_line_bottom_margin, getResources().getDimension(R.dimen.tab_view_top_line_bottom_margin));
		tabBottomLineHeight = typedArray.getDimension(R.styleable.TabView_bottom_line_height, getResources().getDimension(R.dimen.tab_view_bottom_line_height));
		tabBottomLineColor = typedArray.getColor(R.styleable.TabView_bottom_line_color, ContextCompat.getColor(context, R.color.tab_view_bottom_line_color));
		tabBottomLineLeftMargin = typedArray.getDimension(R.styleable.TabView_bottom_line_left_margin, getResources().getDimension(R.dimen.tab_view_bottom_line_left_margin));
		tabBottomLineRightMargin = typedArray.getDimension(R.styleable.TabView_bottom_line_right_margin, getResources().getDimension(R.dimen.tab_view_bottom_line_right_margin));
		tabBottomLineTopMargin = typedArray.getDimension(R.styleable.TabView_bottom_line_top_margin, getResources().getDimension(R.dimen.tab_view_bottom_line_top_margin));
		tabBottomLineBottomMargin = typedArray.getDimension(R.styleable.TabView_bottom_line_bottom_margin, getResources().getDimension(R.dimen.tab_view_bottom_line_bottom_margin));
		tabSplitLineWidth = typedArray.getDimension(R.styleable.TabView_split_line_width, getResources().getDimension(R.dimen.tab_view_split_line_width));
		tabSplitLineColor = typedArray.getColor(R.styleable.TabView_split_line_color, ContextCompat.getColor(context, R.color.tab_view_split_line_color));
		tabSplitLineLeftMargin = typedArray.getDimension(R.styleable.TabView_split_line_left_margin, getResources().getDimension(R.dimen.tab_view_split_left_margin));
		tabSplitLineRigahtMargin = typedArray.getDimension(R.styleable.TabView_split_line_right_margin, getResources().getDimension(R.dimen.tab_view_split_right_margin));
		tabSplitLineTopMargin = typedArray.getDimension(R.styleable.TabView_split_line_top_margin, getResources().getDimension(R.dimen.tab_view_split_top_margin));
		tabSplitLineBottomMargin = typedArray.getDimension(R.styleable.TabView_split_line_bottom_margin, getResources().getDimension(R.dimen.tab_view_split_bottom_margin));
		splitLineSpaceStartEnd = typedArray.getBoolean(R.styleable.TabView_split_line_space_start_end, true);
		splitLineDrawStartEnd = typedArray.getBoolean(R.styleable.TabView_split_line_draw_start_end, false);
		tabLeftPadding = typedArray.getDimension(R.styleable.TabView_tab_left_padding, getResources().getDimension(R.dimen.tab_view_tab_left_padding));
		tabRightPadding = typedArray.getDimension(R.styleable.TabView_tab_right_padding, getResources().getDimension(R.dimen.tab_view_tab_right_padding));
		tabSelectTextColor = typedArray.getColor(R.styleable.TabView_tab_select_text_color, ContextCompat.getColor(context, R.color.tab_view_select_text_color));
		tabUnselectTextColor = typedArray.getColor(R.styleable.TabView_tab_unselect_text_color, ContextCompat.getColor(context, R.color.tab_view_unselect_text_color));
		tabTextSize = typedArray.getDimension(R.styleable.TabView_tab_text_size, getResources().getDimension(R.dimen.tab_view_tab_text_size));
		typedArray.recycle();
	}

	public <F> void setTabItems(final F... datas) {
		setTabItems(datas == null ? null : Arrays.asList(datas));
	}

	public <F, T> void setTabItems(final List<F> datas) {
		this.datas.clear();
		if(datas != null && datas.size() > 0)
			this.datas.addAll(datas);
		contentLayout.removeAllViews();
		if (datas == null || tabItemCreator == null) {
			contentLayout.setTag(-1);
			return;
		}
		for (int i = 0; i < datas.size(); i++) {
			TabItemView itemView = tabItemCreator.createTabItem(getContext(), i);
			LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
			layoutParams.leftMargin = (int) (tabLeftMargin + 0.5);
			if (splitLineSpaceStartEnd || i != 0)
				layoutParams.leftMargin += tabSplitLineRigahtMargin + tabSplitLineWidth / 2;
			layoutParams.rightMargin = (int) (tabRightMargin + 0.5);
			if (splitLineSpaceStartEnd || i != datas.size() - 1)
				layoutParams.rightMargin += tabSplitLineLeftMargin + tabSplitLineWidth / 2;
			setTabViewSpace(itemView);
			itemView.setLayoutParams(layoutParams);
			itemView.setOnClickListener(this);
			itemView.unselectState(getData(i));
			contentLayout.addView(itemView);
		}
//		setTabSelectIndex((int) contentLayout.getTag());
		contentLayout.post(new Runnable() {
			@Override
			public void run() {
				setTabSelectIndex((int) contentLayout.getTag());
			}
		});
	}

	public int getTabCount() {
		return contentLayout.getChildCount();
	}

	public int getTabSelectIndex(){
		return (int) contentLayout.getTag();
	}

	public void setTabSelectIndex(final int selectIndex){
		if(viewPager != null){
			int prePageIndex = viewPager.getCurrentItem();
			viewPager.setCurrentItem(selectIndex);
			if(viewPager.getCurrentItem() == prePageIndex)
				onTabSelected(selectIndex, tabAlwaysCenter, 0);
		} else{
			onTabSelected(selectIndex, tabAlwaysCenter, 0);
		}
	}

	public TabItemView getTabAt(final int position) {
		if (position < 0 || position >= getTabCount())
			return null;
		return (TabItemView) contentLayout.getChildAt(position);
	}

	public int indexOfTab(final TabItemView tabItemView){
		if(tabItemView == null)
			return -1;
		return contentLayout.indexOfChild(tabItemView);
	}

	public void setupWithViewPager(final ViewPager viewPager) {
		if(this.viewPager != null)
			this.viewPager.removeOnPageChangeListener(this);
		this.viewPager = viewPager;
		if (this.viewPager != null) {
			this.viewPager.addOnPageChangeListener(this);
			setTabSelectIndex(this.viewPager.getCurrentItem());
		}
	}

	public void setOnTabItemClickListener(final TabItemClickListener tabItemClickListener){
		this.tabItemClickListener = tabItemClickListener;
	}

	public void setOnDrawCallback(final DrawCallback drawCallback){
		this.drawCallback = drawCallback;
	}

	@Override
	public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
		if(position < 0 || getTabCount() == 0 || position >= getTabCount()){
			this.positionOffset = -1;
		} else {
			if((int) contentLayout.getTag() != viewPager.getCurrentItem())
				onTabSelected(viewPager.getCurrentItem(), tabAlwaysCenter, 1);
			if(tabIndicatorType != 0)
				return;
			this.positionOffset = position + positionOffset;
//			if (positionOffset < 0.5) {
//				getTabAt(position).selectState(getData(position));
//				if (getTabCount() > position + 1)
//					getTabAt(position + 1).unselectState(getData(position + 1));
//			} else {
//				getTabAt(position).unselectState(getData(position));
//				if (getTabCount() > position + 1)
//					getTabAt(position + 1).selectState(getData(position + 1));
//			}
			int selectPosition = (positionOffset < 0.5) ? position : position + 1;
			for(int i = 0; i < getTabCount(); i++)
				if(i != selectPosition)
					getTabAt(i).unselectState(getData(i));
			getTabAt(selectPosition).selectState(getData(selectPosition));

			if(valueAnimator != null && valueAnimator.isStarted())
				valueAnimator.cancel();
			if(tabAlwaysCenter)
				scrollToTab(this.positionOffset);
		}
		invalidate();
	}

	@Override
	public void onPageSelected(int position) {
		onTabSelected(position, false, tabIndicatorType != 0 ? 0 : -1);
	}

	@Override
	public void onPageScrollStateChanged(int state) {}

	@Override
	public void onClick(View view) {
		int preIndex = getTabSelectIndex();
		int index = indexOfTab((TabItemView) view);
		if(tabItemClickListener != null){
			if(preIndex == index)
				tabItemClickListener.onTabReclick(index);
			else
				tabItemClickListener.onTabClick(preIndex, index);
		}
		setTabSelectIndex(index);
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		if(contentLayout.getMeasuredWidth() < getMeasuredWidth()){
			float space = getMeasuredWidth() - contentLayout.getMeasuredWidth();
			int marginSpace = (int) (space / getTabCount() / 2/* + 0.5f*/);
			for(int i = 0; i < getTabCount(); i++){
				TabItemView tabItemView = getTabAt(i);
				LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) tabItemView.getLayoutParams();
				layoutParams.leftMargin += marginSpace;
				layoutParams.rightMargin += marginSpace;
			}
		}
	}

	@Override
	protected void onDraw(Canvas canvas) {
		if(drawCallback != null)
			drawCallback.drawStart(canvas, positionOffset);
		super.onDraw(canvas);
		if (tabTopLineLeftMargin + tabTopLineRightMargin < getWidth() && tabTopLineHeight > 0) {
			paint.setColor(tabTopLineColor);
			canvas.drawRect(tabTopLineLeftMargin + getScrollX(),
					tabTopLineTopMargin,
					getWidth() - tabTopLineRightMargin + getScrollX(),
					tabTopLineTopMargin + tabTopLineHeight,
					paint);
		}
		if (tabBottomLineLeftMargin + tabBottomLineRightMargin < getWidth() && tabBottomLineHeight > 0) {
			paint.setColor(tabBottomLineColor);
			canvas.drawRect(tabBottomLineLeftMargin + getScrollX(),
					getHeight() - tabBottomLineBottomMargin - tabBottomLineHeight,
					getWidth() - tabBottomLineRightMargin + getScrollX(),
					getHeight() - tabBottomLineBottomMargin,
					paint);
		}
		if (tabSplitLineWidth > 0 && tabSplitLineTopMargin + tabSplitLineBottomMargin < getHeight() && getTabCount() > 0) {
			paint.setColor(tabSplitLineColor);
			for (int i = 0; i < getTabCount() - 1; i++) {
				TabItemView tab = getTabAt(i);
				LinearLayout.LayoutParams tabLayoutParams = (LinearLayout.LayoutParams) tab.getLayoutParams();
				float x = tab.getX() + tab.getWidth() + tabLayoutParams.rightMargin - tabSplitLineWidth / 2;
				canvas.drawRect(x, tabSplitLineTopMargin, x + tabSplitLineWidth, getHeight() - tabSplitLineBottomMargin, paint);
			}
			if (splitLineSpaceStartEnd && splitLineDrawStartEnd) {
				TabItemView tab = getTabAt(getTabCount() - 1);
				LinearLayout.LayoutParams tabLayoutParams = (LinearLayout.LayoutParams) tab.getLayoutParams();
				float x = tab.getX() + tab.getWidth() + tabLayoutParams.rightMargin - tabSplitLineWidth / 2;
				canvas.drawRect(0, tabSplitLineTopMargin, tabSplitLineWidth / 2, getHeight() - tabSplitLineBottomMargin, paint);
				canvas.drawRect(x, tabSplitLineTopMargin, x + tabSplitLineWidth / 2, getHeight() - tabSplitLineBottomMargin, paint);
			}
		}
		if (tabIndicatorHeight > 0 && positionOffset >= 0 && ((int) (positionOffset)) < getTabCount()) {
			paint.setColor(tabIndicatorColor);
			IndicatorBean indicatorBean = calculateIndicator(positionOffset, tabIndicatorGravity);
			switch (tabIndicatorAnimator){
				case INDICATOR_LINE_SCALING:
					drawIndicatorWithLineScaling(canvas, paint, indicatorBean);
					break;
				case INDICATOR_LINE_TRANSLATE_FILL:
					drawIndicatorWithLineTranslateFill(canvas, paint, indicatorBean);
					break;
				case INDICATOR_LINE_SCALING_FILL:
					drawIndicatorWithLineScalingFill(canvas, paint, indicatorBean);
					break;
				case INDICATOR_CIRCLE:
					drawIndicatorWithCircle(canvas, paint, indicatorBean);
					break;
				case INDICATOR_TRIANGLE:
					drawIndicatorWithTriangle(canvas, paint, indicatorBean);
					break;
				case INDICATOR_LINE_TRANSLATE:
				default:
					drawIndicatorWithLineTranslate(canvas, paint, indicatorBean);
			}
		}
		if(drawCallback != null)
			drawCallback.drawFinish(canvas, positionOffset);
	}

//	private void setTabViewSpace() {
//		int paddingLeft = getPaddingLeft();
//		int paddingRight = getPaddingRight();
//		int paddingTop = getPaddingTop();
//		int paddingBottom = getPaddingBottom();
//		paddingTop += tabTopLineHeight + tabTopLineTopMargin + tabTopLineBottomMargin;
//		paddingBottom += tabBottomLineHeight + tabBottomLineTopMargin + tabBottomLineBottomMargin;
//		if (tabIndicatorGravity == INDICATOR_BOTTOM)
//			paddingBottom += tabIndicatorHeight + tabIndicatorTopMargin + tabIndicatorBottomMargin;
//		else
//			paddingTop += tabIndicatorHeight + tabIndicatorTopMargin + tabIndicatorBottomMargin;
//		setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
//	}

	private void setTabViewSpace(TabItemView itemView) {
		int paddingLeft = itemView.getPaddingLeft();
		int paddingRight = itemView.getPaddingRight();
		int paddingTop = itemView.getPaddingTop();
		int paddingBottom = itemView.getPaddingBottom();
		paddingTop += tabTopLineHeight + tabTopLineTopMargin + tabTopLineBottomMargin;
		paddingBottom += tabBottomLineHeight + tabBottomLineTopMargin + tabBottomLineBottomMargin;
		if (tabIndicatorGravity == 0)
			paddingBottom += tabIndicatorHeight + tabIndicatorTopMargin + tabIndicatorBottomMargin;
		else
			paddingTop += tabIndicatorHeight + tabIndicatorTopMargin + tabIndicatorBottomMargin;
		itemView.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
	}

	private void initDefaultTabAdapter() {
		tabItemCreator = new TabItemCreator<String, String, TabItem<String>>() {
			@Override
			public TabItem<String> createTabItem(Context context, int position) {
				return new TabItem<>(context);
			}
		};
	}

	private void scrollToTab(float positionOffset){
		if(positionOffset >= 0 && positionOffset <= getTabCount() - 1) {
			IndicatorBean indicatorBean = calculateIndicator(positionOffset, tabIndicatorGravity);
			int x = (int) (indicatorBean.getIndicatorCenterX() - getWidth() / 2 + 0.5);
			smoothScrollTo(x, 0);
		}
	}

	private void startIndicatorAnimator(int index){
		if(valueAnimator != null && valueAnimator.isStarted())
			valueAnimator.cancel();
		valueAnimator = ValueAnimator.ofFloat(positionOffset, index);
		valueAnimator.setDuration(tabIndicatorAnimatorDuration);
		valueAnimator.addUpdateListener(this);
		valueAnimator.start();
	}

	private void onTabSelected(int position, boolean isScrollToCenter, int indicator){
		int preSelectPosition = (int) contentLayout.getTag();
		contentLayout.setTag(position);
		for(int i = 0; i < getTabCount(); i++)
			if(i != position)
				getTabAt(i).unselectState(getData(i));
		if(position >= 0 && position < getTabCount())
			getTabAt(position).selectState(getData(position));
		if(isScrollToCenter)
			scrollToTab(position);
		if(position != positionOffset){
			if(indicator == 0)
				startIndicatorAnimator(position);
			else if(indicator == 1)
				positionOffset = position;
		}
		invalidate();
	}

	private Object getData(int position){
		try{
			return tabItemCreator.transform(datas.get(position));
		} catch (Exception e){
			L.e(e.toString());
			return null;
		}
	}

	private void drawIndicatorWithLineTranslate(Canvas canvas, Paint paint, IndicatorBean indicatorBean) {
		float indicatorStartX = indicatorBean.getTabStartX_1()
				+ (indicatorBean.getTabStartX_2() - indicatorBean.getTabStartX_1()) * indicatorBean.getOffset();
		float indicatorEndX = indicatorBean.getTabEndX_1()
				+ (indicatorBean.getTabEndX_2() - indicatorBean.getTabEndX_1()) * indicatorBean.getOffset();
		canvas.drawRect(indicatorStartX, indicatorBean.getIndicatorStartY(), indicatorEndX, indicatorBean.getIndicatorEndY(), paint);
	}

	private void drawIndicatorWithLineTranslateFill(Canvas canvas, Paint paint, IndicatorBean indicatorBean) {
		float indicatorStartX = indicatorBean.getTabStartFillX_1()
				+ (indicatorBean.getTabStartFillX_2() - indicatorBean.getTabStartFillX_1()) * indicatorBean.getOffset();
		float indicatorEndX = indicatorBean.getTabEndFillX_1()
				+ (indicatorBean.getTabEndFillX_2() - indicatorBean.getTabEndFillX_1()) * indicatorBean.getOffset();
		canvas.drawRect(indicatorStartX, indicatorBean.getIndicatorStartY(), indicatorEndX, indicatorBean.getIndicatorEndY(), paint);
	}

	private void drawIndicatorWithLineScaling(Canvas canvas, Paint paint, IndicatorBean indicatorBean) {
		float indicatorStartX = (indicatorBean.getOffset() < 0.5)
				? indicatorBean.getTabStartX_1()
				: indicatorBean.getTabStartX_1() + (indicatorBean.getTabStartX_2() - indicatorBean.getTabStartX_1()) * indicatorBean.getOffset() * 2;
		float indicatorEndX = (indicatorBean.getOffset() < 0.5)
				? indicatorBean.getTabEndX_1() + (indicatorBean.getTabEndX_2() - indicatorBean.getTabEndX_1()) * indicatorBean.getOffset() * 2
				: indicatorBean.getTabEndX_2();
		canvas.drawRect(indicatorStartX, indicatorBean.getIndicatorStartY(),
				indicatorEndX, indicatorBean.getIndicatorEndY(), paint);
	}

	private void drawIndicatorWithLineScalingFill(Canvas canvas, Paint paint, IndicatorBean indicatorBean) {
		float indicatorStartX = (indicatorBean.getOffset() < 0.5)
				? indicatorBean.getTabStartFillX_1()
				: indicatorBean.getTabStartFillX_1() + (indicatorBean.getTabStartFillX_2() - indicatorBean.getTabStartFillX_1()) * indicatorBean.getOffset() * 2;
		float indicatorEndX = (indicatorBean.getOffset() < 0.5)
				? indicatorBean.getTabEndFillX_1() + (indicatorBean.getTabEndFillX_2() - indicatorBean.getTabEndFillX_1()) * indicatorBean.getOffset() * 2
				: indicatorBean.getTabEndFillX_2();
		canvas.drawRect(indicatorStartX, indicatorBean.getIndicatorStartY(),
				indicatorEndX, indicatorBean.getIndicatorEndY(), paint);
	}

	private void drawIndicatorWithCircle(Canvas canvas, Paint paint, IndicatorBean indicatorBean){
		float radius = indicatorBean.getIndicatorHeight() / 2;
		float centerX = indicatorBean.getIndicatorCenterX();
		float centerY = indicatorBean.getIndicatorCenterY();
		canvas.drawCircle(centerX, centerY, radius, paint);
	}

	private void drawIndicatorWithTriangle(Canvas canvas, Paint paint, IndicatorBean indicatorBean){
		float centerX = indicatorBean.getIndicatorCenterX();
		float leftX = centerX - indicatorBean.getIndicatorHeight();
		float rightX = centerX + indicatorBean.getIndicatorHeight();
		Path path = new Path();
		if(tabIndicatorGravity == 0){
			path.moveTo(leftX, indicatorBean.getIndicatorEndY());
			path.lineTo(rightX, indicatorBean.getIndicatorEndY());
			path.lineTo(centerX, indicatorBean.getIndicatorStartY());
		} else{
			path.moveTo(leftX, indicatorBean.getIndicatorStartY());
			path.lineTo(rightX, indicatorBean.getIndicatorStartY());
			path.lineTo(centerX, indicatorBean.getIndicatorEndY());
		}
		canvas.drawPath(path, paint);
	}

	private IndicatorBean calculateIndicator(float positionOffset, int gravityType) {
		IndicatorBean indicatorBean = new IndicatorBean();
		if(getTabCount() <= 0 || positionOffset < -1 || positionOffset >= getTabCount()){
			if(positionOffset >= getTabCount()) {
				TabItemView tab = getTabAt(getTabCount() - 1);
				LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) tab.getLayoutParams();
				indicatorBean.setTabStartX_1(tab.getX() + tab.getWidth() + layoutParams.rightMargin + tabIndicatorHeight / 2);
			} else{
				indicatorBean.setTabStartX_1(-tabIndicatorHeight / 2);
			}
			indicatorBean.setTabEndX_1(indicatorBean.getTabStartX_1());
			indicatorBean.setTabStartFillX_1(indicatorBean.getTabStartX_1());
			indicatorBean.setTabEndFillX_1(indicatorBean.getTabStartX_1());
			indicatorBean.setTabCenterX_1(indicatorBean.getTabStartX_1());
			indicatorBean.setTabWidth_1(0);
			indicatorBean.setTabStartX_2(indicatorBean.getTabStartX_1());
			indicatorBean.setTabEndX_2(indicatorBean.getTabEndX_2());
			indicatorBean.setTabStartFillX_2(indicatorBean.getTabStartFillX_2());
			indicatorBean.setTabEndFillX_2(indicatorBean.getTabEndFillX_2());
			indicatorBean.setTabCenterX_2(indicatorBean.getTabCenterX_2());
			indicatorBean.setTabWidth_2(indicatorBean.getTabWidth_1());
		} else if(positionOffset < 0){
			indicatorBean.setTabStartX_1(-tabIndicatorHeight / 2);
			indicatorBean.setTabEndX_1(indicatorBean.getTabStartX_1());
			indicatorBean.setTabStartFillX_1(indicatorBean.getTabStartX_1());
			indicatorBean.setTabEndFillX_1(indicatorBean.getTabStartX_1());
			indicatorBean.setTabCenterX_1(indicatorBean.getTabStartX_1());
			indicatorBean.setTabWidth_1(0);
			TabItemView tab_2 = getTabAt(0);
			LinearLayout.LayoutParams layoutParams_2 = (LinearLayout.LayoutParams) tab_2.getLayoutParams();
			indicatorBean.setTabStartX_2(tab_2.getX());
			indicatorBean.setTabEndX_2(indicatorBean.getTabStartX_2() + tab_2.getWidth());
			indicatorBean.setTabStartFillX_2(indicatorBean.getTabStartX_2() - layoutParams_2.leftMargin + tabSplitLineWidth / 2 + tabSplitLineRigahtMargin);
			indicatorBean.setTabEndFillX_2(indicatorBean.getTabEndX_2() + layoutParams_2.rightMargin - tabSplitLineWidth / 2 - tabSplitLineRigahtMargin);
			indicatorBean.setTabCenterX_2(indicatorBean.getTabStartX_2() + (indicatorBean.getTabEndX_2() - indicatorBean.getTabStartX_2()) / 2);
			indicatorBean.setTabWidth_2(tab_2.getWidth());
		} else if(positionOffset >= 0 && positionOffset < getTabCount()){
			TabItemView tab_1 = getTabAt((int) positionOffset);
			LinearLayout.LayoutParams layoutParams_1 = (LinearLayout.LayoutParams) tab_1.getLayoutParams();
			indicatorBean.setTabStartX_1(tab_1.getX());
			indicatorBean.setTabEndX_1(indicatorBean.getTabStartX_1() + tab_1.getWidth());
			indicatorBean.setTabStartFillX_1(indicatorBean.getTabStartX_1() - layoutParams_1.leftMargin + tabSplitLineWidth / 2 + tabSplitLineRigahtMargin);
			indicatorBean.setTabEndFillX_1(indicatorBean.getTabEndX_1() + layoutParams_1.rightMargin - tabSplitLineWidth / 2 - tabSplitLineRigahtMargin);
			indicatorBean.setTabCenterX_1(indicatorBean.getTabStartX_1() + (indicatorBean.getTabEndX_1() - indicatorBean.getTabStartX_1()) / 2);
			indicatorBean.setTabWidth_1(tab_1.getWidth());
			if (positionOffset + 1 < getTabCount()) {
				TabItemView tab_2 = getTabAt((int) (positionOffset + 1));
				LinearLayout.LayoutParams layoutParams_2 = (LinearLayout.LayoutParams) tab_2.getLayoutParams();
				indicatorBean.setTabStartX_2(tab_2.getX());
				indicatorBean.setTabEndX_2(indicatorBean.getTabStartX_2() + tab_2.getWidth());
				indicatorBean.setTabStartFillX_2(indicatorBean.getTabStartX_2() - layoutParams_2.leftMargin + tabSplitLineWidth / 2 + tabSplitLineRigahtMargin);
				indicatorBean.setTabEndFillX_2(indicatorBean.getTabEndX_2() + layoutParams_2.rightMargin - tabSplitLineWidth / 2 - tabSplitLineRigahtMargin);
				indicatorBean.setTabCenterX_2(indicatorBean.getTabStartX_2() + (indicatorBean.getTabEndX_2() - indicatorBean.getTabStartX_2()) / 2);
				indicatorBean.setTabWidth_2(tab_2.getWidth());
			} else {
				indicatorBean.setTabStartX_2(indicatorBean.getTabEndX_1() + layoutParams_1.rightMargin + tabIndicatorHeight / 2);
				indicatorBean.setTabEndX_2(indicatorBean.getTabStartX_2());
				indicatorBean.setTabStartFillX_2(indicatorBean.getTabStartX_2());
				indicatorBean.setTabEndFillX_2(indicatorBean.getTabStartX_2());
				indicatorBean.setTabCenterX_2(indicatorBean.getTabStartX_2());
				indicatorBean.setTabWidth_2(0);
			}
		}
		indicatorBean.setTabCenterX(indicatorBean.getTabCenterX_1() + (indicatorBean.getTabCenterX_2() - indicatorBean.getTabCenterX_1()) / 2);
		indicatorBean.setOffset(positionOffset - (int) positionOffset);
		indicatorBean.setIndicatorCenterX(indicatorBean.getTabCenterX_1()
				+ (indicatorBean.getTabCenterX_2() - indicatorBean.getTabCenterX_1())
				* indicatorBean.getOffset()
		);
		float indicatorStartY;
		if (gravityType == 0)
			indicatorStartY = getHeight() - tabBottomLineBottomMargin - tabBottomLineHeight
					- tabBottomLineTopMargin - tabIndicatorBottomMargin - tabIndicatorHeight;
		else
			indicatorStartY = tabTopLineTopMargin + tabTopLineHeight + tabTopLineBottomMargin + tabIndicatorTopMargin;
		float indicatorEndY = indicatorStartY + tabIndicatorHeight;
		indicatorBean.setIndicatorStartY(indicatorStartY);
		indicatorBean.setIndicatorEndY(indicatorEndY);
		indicatorBean.setIndicatorHeight(tabIndicatorHeight);
		indicatorBean.setIndicatorCenterY(indicatorStartY + tabIndicatorHeight / 2);
		return indicatorBean;
	}

	@Override
	public void onAnimationUpdate(ValueAnimator animation) {
		positionOffset = (float) animation.getAnimatedValue();
		invalidate();
	}

	private class IndicatorBean {
		private float tabStartX_1;
		private float tabStartFillX_1;
		private float tabEndX_1;
		private float tabEndFillX_1;
		private float tabCenterX_1;
		private float tabWidth_1;
		private float tabStartX_2;
		private float tabStartFillX_2;
		private float tabEndX_2;
		private float tabEndFillX_2;
		private float tabCenterX_2;
		private float tabWidth_2;
		private float tabCenterX;
		private float indicatorCenterX;
		private float indicatorCenterY;
		private float indicatorStartY;
		private float indicatorEndY;
		private float indicatorHeight;
		private float offset;
		public float getTabStartX_1() {
			return tabStartX_1;
		}
		public void setTabStartX_1(float tabStartX_1) {
			this.tabStartX_1 = tabStartX_1;
		}
		public float getTabStartFillX_1() {
			return tabStartFillX_1;
		}
		public void setTabStartFillX_1(float tabStartFillX_1) {
			this.tabStartFillX_1 = tabStartFillX_1;
		}
		public float getTabEndX_1() {
			return tabEndX_1;
		}
		public void setTabEndX_1(float tabEndX_1) {
			this.tabEndX_1 = tabEndX_1;
		}
		public float getTabEndFillX_1() {
			return tabEndFillX_1;
		}
		public void setTabEndFillX_1(float tabEndFillX_1) {
			this.tabEndFillX_1 = tabEndFillX_1;
		}
		public float getTabCenterX_1() {
			return tabCenterX_1;
		}
		public void setTabCenterX_1(float tabCenterX_1) {
			this.tabCenterX_1 = tabCenterX_1;
		}
		public float getTabWidth_1() {
			return tabWidth_1;
		}
		public void setTabWidth_1(float tabWidth_1) {
			this.tabWidth_1 = tabWidth_1;
		}
		public float getTabStartX_2() {
			return tabStartX_2;
		}
		public void setTabStartX_2(float tabStartX_2) {
			this.tabStartX_2 = tabStartX_2;
		}
		public float getTabStartFillX_2() {
			return tabStartFillX_2;
		}
		public void setTabStartFillX_2(float tabStartFillX_2) {
			this.tabStartFillX_2 = tabStartFillX_2;
		}
		public float getTabEndX_2() {
			return tabEndX_2;
		}
		public void setTabEndX_2(float tabEndX_2) {
			this.tabEndX_2 = tabEndX_2;
		}
		public float getTabEndFillX_2() {
			return tabEndFillX_2;
		}
		public void setTabEndFillX_2(float tabEndFillX_2) {
			this.tabEndFillX_2 = tabEndFillX_2;
		}
		public float getTabCenterX_2() {
			return tabCenterX_2;
		}
		public void setTabCenterX_2(float tabCenterX_2) {
			this.tabCenterX_2 = tabCenterX_2;
		}
		public float getTabWidth_2() {
			return tabWidth_2;
		}
		public void setTabWidth_2(float tabWidth_2) {
			this.tabWidth_2 = tabWidth_2;
		}
		public float getTabCenterX() {
			return tabCenterX;
		}
		public void setTabCenterX(float tabCenterX) {
			this.tabCenterX = tabCenterX;
		}
		public float getIndicatorCenterX() {
			return indicatorCenterX;
		}
		public void setIndicatorCenterX(float indicatorCenterX) {
			this.indicatorCenterX = indicatorCenterX;
		}

		public float getIndicatorCenterY() {
			return indicatorCenterY;
		}
		public void setIndicatorCenterY(float indicatorCenterY) {
			this.indicatorCenterY = indicatorCenterY;
		}
		public float getIndicatorStartY() {
			return indicatorStartY;
		}
		public void setIndicatorStartY(float indicatorStartY) {
			this.indicatorStartY = indicatorStartY;
		}
		public float getIndicatorEndY() {
			return indicatorEndY;
		}
		public void setIndicatorEndY(float indicatorEndY) {
			this.indicatorEndY = indicatorEndY;
		}
		public float getIndicatorHeight() {
			return indicatorHeight;
		}
		public void setIndicatorHeight(float indicatorHeight) {
			this.indicatorHeight = indicatorHeight;
		}
		public float getOffset() {
			return offset;
		}
		public void setOffset(float offset) {
			this.offset = offset;
		}
	}

	public class TabItem<T extends String> extends TabItemView<T> {

		private TextView textView;
		private int selectTextColor;
		private int unselectTextColor;

		public TabItem(Context context) {
			super(context);
			selectTextColor = tabSelectTextColor;
			unselectTextColor = tabUnselectTextColor;
		}

		public void setSelectTextColor(int selectTextColor) {
			this.selectTextColor = selectTextColor;
			if(getTabAt(getTabSelectIndex()) == this)
				textView.setTextColor(this.selectTextColor);
		}

		public void setUnselectTextColor(int unselectTextColor) {
			this.unselectTextColor = unselectTextColor;
			if(getTabAt(getTabSelectIndex()) != this)
				textView.setTextColor(this.unselectTextColor);
		}

		@NonNull
		@Override
		protected View setView(Context context) {
			textView = new TextView(context);
			textView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
			textView.setPadding((int) (tabLeftPadding + 0.5), 0, (int) (tabRightPadding + 0.5), 0);
			textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, tabTextSize);
			textView.setGravity(Gravity.CENTER);
			textView.setTag(false);
			return textView;
		}

		@Override
		public void selectState(String text) {
			textView.setTag(true);
			textView.setText(text);
			textView.setTextColor(selectTextColor);
		}

		@Override
		public void unselectState(String text) {
			textView.setTag(false);
			textView.setText(text);
			textView.setTextColor(unselectTextColor);
		}

		public void setText(String text) {
			textView.setText(text);
		}

		public String getText() {
			if (textView.getText() == null)
				return null;
			return textView.getText().toString();
		}

		public TextView getTextView() {
			return textView;
		}

		private void updateText() {
			if ((boolean) textView.getTag())
				selectState(getText());
			else
				unselectState(getText());
		}
	}

	public static abstract class TabItemCreator<F, T, V extends TabItemView<T>> {

		public T transform(F data) {
			return (T) data;
		}

		public abstract V createTabItem(Context context, int position);
	}

	public static abstract class TabItemView<T> extends FrameLayout {

		private View contentView;

		public TabItemView(Context context) {
			super(context);
			init(context);
		}

		public TabItemView(Context context, AttributeSet attrs) {
			super(context, attrs);
			init(context);
		}

		public TabItemView(Context context, AttributeSet attrs, int defStyleAttr) {
			super(context, attrs, defStyleAttr);
			init(context);
		}

		private void init(Context context) {
			setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
			contentView = setView(context);
			addView(contentView);
		}

		public View getContentView() {
			return contentView;
		}

		public <V extends View> V getView(int id) {
			if (contentView == null)
				return null;
			return (V) contentView.findViewById(id);
		}

		@NonNull
		abstract protected View setView(Context context);

		abstract public void selectState(T data);

		abstract public void unselectState(T data);
	}

	public interface TabItemClickListener{
		void onTabClick(int preIndex, int selecIndex);
		void onTabReclick(int selectIndex);
	}

	public interface DrawCallback{
		void drawStart(Canvas canvas, float positionOffset);
		void drawFinish(Canvas canvas, float positionOffset);
	}
}
